﻿/*
 * Twipler - Twitter Web Client with features.
 *  
 *  Copyright (C) 2009, 2010. Ian Quigley
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details. 
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */


using System.Linq;
using System.Text;
using NHibernate.Criterion;
using Twipler.Accounts;
using Twipler.App.Render;
using Twipler.App.DataDoc;
using Dimebrain.TweetSharp.Fluent;
using System.Collections.Generic;
using Twipler.App.Parse.Code;
using System;
using Twipler.Data.Access;
using Twipler.Data.Cache;
using Twipler.Data.Entity.Twipler;
using Twipler.WebApp;

namespace Twipler.App.Entity
{
    public class Me : ICallableClass
    {
        public static IDataDoc Followers()
        {
			return Current.SessionTrack.ApiCaller.Request(t => t.Users().GetFollowers().CreateCursor(), true); 
        }

        public static IDataDoc Following()
        {   
            return Current.SessionTrack.ApiCaller.Request(t => t.Users().GetFriends().CreateCursor(), true); 
        }

        public static IDataDoc Following(long cursor)
        {
            return Current.SessionTrack.ApiCaller.Request(t => t.Users().GetFriends().GetCursor(cursor).CreateCursor(), true);
        }

        public static IDataDoc FriendsNotFollowing(int page, int take)
        {
			if (page < 1)
				page = 1;
			
			if (take < 1)
				take = 1;

			using (TableStore<Friend> friends = TableStore<Friend>.Open(Current.Visitor.Id))
			using (TableStore<Follower> followers = TableStore<Follower>.Open(Current.Visitor.Id))
        	{
        		List<Friend> celebs = friends.ReadAllExcluding(followers);
        		return new DataDocList(new List<DataDocValue>()
        			                	{
        			                		new DataDocValue("users", SyntheticUserList(celebs.Skip((page - 1)*take).Take(take)))
        			                	});
        	}
        }

		public static IDataDoc FollowingNotFriends(int page, int take)
		{
			if (page < 1)
				page = 1;

			if (take < 1)
				take = 1;

			using (TableStore<Friend> friends = TableStore<Friend>.Open(Current.Visitor.Id))
			using (TableStore<Follower> followers = TableStore<Follower>.Open(Current.Visitor.Id))
			{
				List<Follower> boring = followers.ReadAllExcluding(friends);
				return new DataDocList(new List<DataDocValue>()
        			                	{
        			                		new DataDocValue("users", SyntheticUserList(boring.Skip((page - 1)*take).Take(take)))
        			                	});
			}
		}
        
        public static IDataDoc FollowingLists()
        {
            return Current.SessionTrack.ApiCaller.Request(t => t.Lists().GetSubscriptions(App.Current.Visitor.ScreenName.ToLower()), false);
        }

        public static IDataDoc Mentions()
        {
            return Mentions(1, 20);
        }

        public static IDataDoc Mentions(int page, int take)
        {
            return Current.SessionTrack.ApiCaller.Request(t => t.Statuses().Mentions().Skip(page).Take(take), true);
        }

        public static IDataDoc Details()
        {
            return App.Current.Visitor.Details;
        }

		public static IDataDoc AliasList()
		{
			List<Visitor> alias = Current.Visitor.Alias;

			List<DataDocValue> list = new List<DataDocValue>();

			using (TableStore<TwitterUserDetails> details = TableStore<TwitterUserDetails>.Open())
			{
				int i = 0;
				foreach (Visitor account in alias)
				{
					TwitterUserDetails detial = details.Find(account.Id);
					if (detial == null)
						continue;

					list.Add(new DataDocValue(i.ToString(), new List<DataDocValue>
					                                        	{
					                                        		new DataDocValue("twitter_id", account.Id),
					                                        		new DataDocValue("screen_name", account.ScreenName),
																	new DataDocValue("profile_image_url", detial.ProfileImageUrl),
					                                        		new DataDocValue("_primary", account.IsPrimaryIdentity)
					                                        	}));
					i++;
				}
			}

			return new DataDocList(new List<DataDocValue>() { new DataDocValue("", list), new DataDocValue("count", alias.Count() + 1) });
		}

    	public static IDataDoc UnfollowedBy(int page, int take)
        {
            List<Unfollower> unfollowed;
			using (TableStore<Unfollower> myUnfollowers = TableStore<Unfollower>.Open(Current.SessionTrack.Visitor.Id))
            {
                unfollowed = myUnfollowers.ReadAll();
                
                // validate and twiddle the numbers
                if (take > 20)
                    take = 20;

                if (page < 1)
                    page = 1;

                page = (page - 1) * take;

                if (page >= unfollowed.Count)
                    return new DataDocList(DataDocStatus.TwiplerError, "Page beyond size of result set.");

                int limit = take;
                if ((unfollowed.Count - page) < take)
                    limit = (unfollowed.Count - page);

                List<Unfollower> showUsers = new List<Unfollower>();

                for (int i = page; i < page + limit; i++) // unfollowed.GetRange(page, limit);
                    showUsers.Add(myUnfollowers.Find(unfollowed[i].Id));

                // Get the User information
                StringBuilder urlLookupUser = new StringBuilder();
                urlLookupUser.Append("users/lookup.json?user_id=");

                foreach (Unfollower unfollower in showUsers)
                    urlLookupUser.AppendFormat("{0},", unfollower.Id);

                urlLookupUser.Remove(urlLookupUser.Length - 1, 1);

                DataDocJson results = new DataDocJson(() => Current.SessionTrack.ApiCaller.RequestRaw(t => t.Direct(urlLookupUser.ToString()), true));
                if (results.Status != DataDocStatus.Ok)
                    results = new DataDocJson(() => "{}");

                // Reduce "show" list to users not displayed from above TwitterAPI call
                foreach (IDataDoc user in results.Select("/"))
                {
                    DataDocValue userId = user.Find("id");
                    if (userId != null)
                        showUsers.Remove(showUsers.Find(uw => uw.Id == (long)userId.Value));
                }

				using (TableStore<TwitterUserDetails> twitterUsers = TableStore<TwitterUserDetails>.Open())
                {
                    // Try find the "zombie" users from our local database
                    foreach (Unfollower zombie in showUsers)
                    {
						List<DataDocValue> zombieDoc = SyntheticUserDoc(twitterUsers, zombie.Id);
						zombieDoc.Add(new DataDocValue("following", "false"));
                    	zombieDoc.Add(new DataDocValue("_is_zombie", "true"));

                        results.Insert("/", new DataDocValue("", zombieDoc));
                    }
                }

                using (TableStore<Unfollower> unfollowers = TableStore<Unfollower>.Open(Current.SessionTrack.Visitor.Id))
                {
                    // Add our custom "unfollowed_since" field
                    foreach (IDataDoc user in results.Select("/"))
                    {
                        Unfollower muppet = unfollowers.Find(Convert.ToInt32(user.Find("id").Value));
                        user.Add("unfollowed_since", muppet.Since);
                    }
                }

                results.SortBy("unfollowed_since", (x, y) => { return (int)((DateTime)y).Subtract((DateTime)x).TotalMinutes; });


                return results;
            }
        }

		private static List<DataDocValue> SyntheticUserList<T>(IEnumerable<T> twitterId) where T : TwitterId
		{
			List<DataDocValue> doc = new List<DataDocValue>();

			int index = 0;
			using (TableStore<TwitterUserDetails> users = TableStore<TwitterUserDetails>.Open())
			{
				foreach (TwitterId id in twitterId)
				{
					List<DataDocValue> userDoc = SyntheticUserDoc(users, id.Id);
					doc.Add(new DataDocValue(index++.ToString(), userDoc));
				}
			}

			return doc;
		}

    	private static List<DataDocValue> SyntheticUserDoc(TableStore<TwitterUserDetails> detailsAdapter, int twitterId)
    	{
    		TwitterUserDetails details = detailsAdapter.Find(twitterId);

    		List<DataDocValue> syntheticUserDoc = new List<DataDocValue>();
    		syntheticUserDoc.Add(new DataDocValue("id", Convert.ToInt64(twitterId)));

			if (details != null)
    		{
				syntheticUserDoc.Add(new DataDocValue("screen_name", details.ScreenName));
				syntheticUserDoc.Add(new DataDocValue("profile_image_url", details.ProfileImageUrl));
    		}
    		else
    		{
    			syntheticUserDoc.Add(new DataDocValue("screen_name", string.Format("Zombie [{0}]", twitterId)));
    			syntheticUserDoc.Add(new DataDocValue("profile_image_url", "http://s.twimg.com/a/1274899949/images/default_profile_4_normal.png"));
    		}
    		
    		syntheticUserDoc.Add(new DataDocValue("followers_count", 0));
    		syntheticUserDoc.Add(new DataDocValue("friends_count", 0));
    		syntheticUserDoc.Add(new DataDocValue("statuses_count", 0));

    		return syntheticUserDoc;
    	}

    	public static IDataDoc SwitchTo(int otherTwitterId)
    	{
			if (Current.Visitor.IsAnonymous)
                return SingleStatus("error");

    		List<Visitor> aliases = Current.Visitor.Alias;

    		Visitor switchTo = aliases.FirstOrDefault( t => t.Id == otherTwitterId );
			if (switchTo == null)
                return SingleStatus("error");

    		Visitor primaryAccount = aliases.FirstOrDefault( t => t.IsPrimaryIdentity );
			if (primaryAccount == null)
                return SingleStatus("error");

    		List<Alias> alias = Factory.Alias.GetByCriteria( Restrictions.Eq( "Id", primaryAccount.Id ),
    		                             Restrictions.Eq( "OtherTwitterId", switchTo.Id ) );

			if (alias.Count != 1)
                return SingleStatus("error");

    		string[] tokeAndSecret = alias[ 0 ].OAuthEnc.Split( ' ' );
			if (tokeAndSecret.Length != 2)
                return SingleStatus("error");

    		Current.SessionTrack.OAuthToken = new Dimebrain.TweetSharp.Model.OAuthToken() {Token = tokeAndSecret[ 0 ], TokenSecret = tokeAndSecret[ 1 ]};

			Visitors.Login(Current.SessionTrack.ApiCaller.Request(t => t.Account().VerifyCredentials(), false));

    		Current.Visitor.Alias = aliases ;

            return SingleStatus("success");
    	}

        private static IDataDoc SingleStatus(string status)
        {
            return new DataDocList(new List<DataDocValue> { new DataDocValue("status", status) });
        }
    }
}